#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <functional>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include "InetAddr.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include "Socket.hpp"
#include <memory>

using namespace std;
using namespace socket_ns;
using http_t = function<string(string requset)>; // 服务器端执行的方法
class TcpServer;

// 线程数据包
class ThreadData
{
public:
    ThreadData(socket_sptr sockfd, InetAddr peer, TcpServer *self)
        : _sockfd(sockfd), _clientserver(peer), _self(self)
    {
    }
    socket_sptr _sockfd;    // TcpSocket类型指针，可以访问到套接字
    InetAddr _clientserver; // 与本服务器通信(连接成功)的客户端网络信息
    TcpServer *_self;       // 指向TcpServer类的指针
};

class TcpServer
{
public:
    // 只要创建出TcpServer对象，就立马构建出本地网络通信，Tcp套接字指针
    TcpServer(int port, http_t http_service)
        : _localaddr("0", port), _listensock(make_unique<TcpSocket>()), _isrunning(false), _http_service(http_service)
    {
        // 创建监听套接字
        _listensock->BulidListenSocket(_localaddr);
    }
    ~TcpServer()
    {
    }

    // 线程执行函数
    // 由于线程执行函数只能拥有一个参数，类中的函数默认具有this指针，所以此处使用static修饰该函数
    static void *HandlerSock(void *args)
    {
        ThreadData *td = static_cast<ThreadData *>(args);
        // 将线程进行分离-主线程不用Join该线程
        pthread_detach(pthread_self());
        // 修饰之后需要使用该类中的Service函数，需要this指针
        // 让该线程执行服务

        // 一次http请求与应答
        string request, responce;
        ssize_t n = td->_sockfd->Recv(&request); // 获取数据-读取请求
        if (n > 0)
        {
            responce = td->_self->_http_service(request); // 处理
            // 发送
            td->_sockfd->Send(responce); // 发送应答
        }

        td->_sockfd->Close();
        // 用完销毁数据包
        delete (td);
        return nullptr;
    }

    // 循环运行服务
    void Loop()
    {
        // 4.不能直接收取数据，首先得获取连接
        _isrunning = true;
        while (_isrunning)
        {
            // 服务器获取连接
            InetAddr peeraddr;
            socket_sptr normalsock = _listensock->Accepter(&peeraddr);
            // 将客户端网络信息填写到peeraddr对象中，返回的normalsock指针可以访问到tcpSocket套接字
            if (normalsock == nullptr)
            {
                // 获取连接失败时，让其继续获取即可
                continue;
            }
            // 版本3-使用多线程
            pthread_t t;                                                 // 创建线程
            ThreadData *td = new ThreadData(normalsock, peeraddr, this); // 线程数据包
            pthread_create(&t, nullptr, HandlerSock, td);                // 创建线程
        }
        _isrunning = false;
    }

private:
    InetAddr _localaddr;            // 本地网络信息
    unique_ptr<Socket> _listensock; // Tcp套接字指针
    bool _isrunning;
    http_t _http_service; // 服务器要执行的方法
};